1   /*
2    * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.nio.cs.ext;
27  
28  import java.nio.ByteBuffer;
29  import java.nio.CharBuffer;
30  import java.nio.charset.Charset;
31  import java.nio.charset.CharsetDecoder;
32  import java.nio.charset.CharsetEncoder;
33  import java.nio.charset.CoderResult;
34  import java.util.Arrays;
35  import sun.nio.cs.Surrogate;
36  import static sun.nio.cs.CharsetMapping.*;
37  
38  /*
39   * Four types of "DoubleByte" charsets are implemented in this class
40   * (1)DoubleByte
41   *    The "mostly widely used" multibyte charset, a combination of
42   *    a singlebyte character set (usually the ASCII charset) and a
43   *    doublebyte character set. The codepoint values of singlebyte
44   *    and doublebyte don't overlap. Microsoft's multibyte charsets
45   *    and IBM's "DBCS_ASCII" charsets, such as IBM1381, 942, 943,
46   *    948, 949 and 950 are such charsets.
47   *
48   * (2)DoubleByte_EBCDIC
49   *    IBM EBCDIC Mix multibyte charset. Use SO and SI to shift (switch)
50   *    in and out between the singlebyte character set and doublebyte
51   *    character set.
52   *
53   * (3)DoubleByte_SIMPLE_EUC
54   *    It's a "simple" form of EUC encoding scheme, only have the
55   *    singlebyte character set G0 and one doublebyte character set
56   *    G1 are defined, G2 (with SS2) and G3 (with SS3) are not used.
57   *    So it is actually the same as the "typical" type (1) mentioned
58   *    above, except it return "malformed" for the SS2 and SS3 when
59   *    decoding.
60   *
61   * (4)DoubleByte ONLY
62   *    A "pure" doublebyte only character set. From implementation
63   *    point of view, this is the type (1) with "decodeSingle" always
64   *    returns unmappable.
65   *
66   * For simplicity, all implementations share the same decoding and
67   * encoding data structure.
68   *
69   * Decoding:
70   *
71   *    char[][] b2c;
72   *    char[] b2cSB;
73   *    int b2Min, b2Max
74   *
75   *    public char decodeSingle(int b) {
76   *        return b2cSB.[b];
77   *    }
78   *
79   *    public char decodeDouble(int b1, int b2) {
80   *        if (b2 < b2Min || b2 > b2Max)
81   *            return UNMAPPABLE_DECODING;
82   *         return b2c[b1][b2 - b2Min];
83   *    }
84   *
85   *    (1)b2Min, b2Max are the corresponding min and max value of the
86   *       low-half of the double-byte.
87   *    (2)The high 8-bit/b1 of the double-byte are used to indexed into
88   *       b2c array.
89   *
90   * Encoding:
91   *
92   *    char[] c2b;
93   *    char[] c2bIndex;
94   *
95   *    public int encodeChar(char ch) {
96   *        return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
97   *    }
98   *
99   */
100 
101 public class DoubleByte {
102 
103     public final static char[] B2C_UNMAPPABLE;
104     static {
105         B2C_UNMAPPABLE = new char[0x100];
106         Arrays.fill(B2C_UNMAPPABLE, UNMAPPABLE_DECODING);
107     }
108 
109     public static class Decoder extends CharsetDecoder
110                                 implements DelegatableDecoder
111     {
112 
113         final char[][] b2c;
114         final char[] b2cSB;
115         final int b2Min;
116         final int b2Max;
117 
118         // for SimpleEUC override
119         protected CoderResult crMalformedOrUnderFlow(int b) {
120             return CoderResult.UNDERFLOW;
121         }
122 
123         protected CoderResult crMalformedOrUnmappable(int b) {
124             return CoderResult.unmappableForLength(2);
125         }
126 
127         Decoder(Charset cs, float avgcpb, float maxcpb,
128                 char[][] b2c, char[] b2cSB,
129                 int b2Min, int b2Max) {
130             super(cs, avgcpb, maxcpb);
131             this.b2c = b2c;
132             this.b2cSB = b2cSB;
133             this.b2Min = b2Min;
134             this.b2Max = b2Max;
135         }
136 
137         Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
138             this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max);
139         }
140 
141         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
142             byte[] sa = src.array();
143             int sp = src.arrayOffset() + src.position();
144             int sl = src.arrayOffset() + src.limit();
145 
146             char[] da = dst.array();
147             int dp = dst.arrayOffset() + dst.position();
148             int dl = dst.arrayOffset() + dst.limit();
149 
150             try {
151                 while (sp < sl && dp < dl) {
152                     // inline the decodeSingle/Double() for better performance
153                     int inSize = 1;
154                     int b1 = sa[sp] & 0xff;
155                     char c = b2cSB[b1];
156                     if (c == UNMAPPABLE_DECODING) {
157                         if (sl - sp < 2)
158                             return crMalformedOrUnderFlow(b1);
159                         int b2 = sa[sp + 1] & 0xff;
160                         if (b2 < b2Min || b2 > b2Max ||
161                             (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
162                             return crMalformedOrUnmappable(b1);
163                         }
164                         inSize++;
165                     }
166                     da[dp++] = c;
167                     sp += inSize;
168                 }
169                 return (sp >= sl) ? CoderResult.UNDERFLOW
170                                   : CoderResult.OVERFLOW;
171             } finally {
172                 src.position(sp - src.arrayOffset());
173                 dst.position(dp - dst.arrayOffset());
174             }
175         }
176 
177         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
178             int mark = src.position();
179             try {
180 
181                 while (src.hasRemaining() && dst.hasRemaining()) {
182                     int b1 = src.get() & 0xff;
183                     char c = b2cSB[b1];
184                     int inSize = 1;
185                     if (c == UNMAPPABLE_DECODING) {
186                         if (src.remaining() < 1)
187                             return crMalformedOrUnderFlow(b1);
188                         int b2 = src.get() & 0xff;
189                         if (b2 < b2Min || b2 > b2Max ||
190                             (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING)
191                             return crMalformedOrUnmappable(b1);
192                         inSize++;
193                     }
194                     dst.put(c);
195                     mark += inSize;
196                 }
197                 return src.hasRemaining()? CoderResult.OVERFLOW
198                                          : CoderResult.UNDERFLOW;
199             } finally {
200                 src.position(mark);
201             }
202         }
203 
204         // Make some protected methods public for use by JISAutoDetect
205         public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
206             if (src.hasArray() && dst.hasArray())
207                 return decodeArrayLoop(src, dst);
208             else
209                 return decodeBufferLoop(src, dst);
210         }
211 
212         public void implReset() {
213             super.implReset();
214         }
215 
216         public CoderResult implFlush(CharBuffer out) {
217             return super.implFlush(out);
218         }
219 
220         // decode loops are not using decodeSingle/Double() for performance
221         // reason.
222         public char decodeSingle(int b) {
223             return b2cSB[b];
224         }
225 
226         public char decodeDouble(int b1, int b2) {
227             if (b2 < b2Min || b2 > b2Max)
228                 return UNMAPPABLE_DECODING;
229             return  b2c[b1][b2 - b2Min];
230         }
231     }
232 
233     // IBM_EBCDIC_DBCS
234     public static class Decoder_EBCDIC extends Decoder {
235         private static final int SBCS = 0;
236         private static final int DBCS = 1;
237         private static final int SO = 0x0e;
238         private static final int SI = 0x0f;
239         private int  currentState;
240 
241         Decoder_EBCDIC(Charset cs,
242                        char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
243             super(cs, b2c, b2cSB, b2Min, b2Max);
244         }
245 
246         public void implReset() {
247             currentState = SBCS;
248         }
249 
250         // Check validity of dbcs ebcdic byte pair values
251         //
252         // First byte : 0x41 -- 0xFE
253         // Second byte: 0x41 -- 0xFE
254         // Doublebyte blank: 0x4040
255         //
256         // The validation implementation in "old" DBCS_IBM_EBCDIC and sun.io
257         // as
258         //            if ((b1 != 0x40 || b2 != 0x40) &&
259         //                (b2 < 0x41 || b2 > 0xfe)) {...}
260         // is not correct/complete (range check for b1)
261         //
262         private static boolean isDoubleByte(int b1, int b2) {
263             return (0x41 <= b1 && b1 <= 0xfe && 0x41 <= b2 && b2 <= 0xfe)
264                    || (b1 == 0x40 && b2 == 0x40); // DBCS-HOST SPACE
265         }
266 
267         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
268             byte[] sa = src.array();
269             int sp = src.arrayOffset() + src.position();
270             int sl = src.arrayOffset() + src.limit();
271             char[] da = dst.array();
272             int dp = dst.arrayOffset() + dst.position();
273             int dl = dst.arrayOffset() + dst.limit();
274 
275             try {
276                 // don't check dp/dl together here, it's possible to
277                 // decdoe a SO/SI without space in output buffer.
278                 while (sp < sl) {
279                     int b1 = sa[sp] & 0xff;
280                     int inSize = 1;
281                     if (b1 == SO) {  // Shift out
282                         if (currentState != SBCS)
283                             return CoderResult.malformedForLength(1);
284                         else
285                             currentState = DBCS;
286                     } else if (b1 == SI) {
287                         if (currentState != DBCS)
288                             return CoderResult.malformedForLength(1);
289                         else
290                             currentState = SBCS;
291                     } else {
292                         char c =  UNMAPPABLE_DECODING;
293                         if (currentState == SBCS) {
294                             c = b2cSB[b1];
295                             if (c == UNMAPPABLE_DECODING)
296                                 return CoderResult.unmappableForLength(1);
297                         } else {
298                             if (sl - sp < 2)
299                                 return CoderResult.UNDERFLOW;
300                             int b2 = sa[sp + 1] & 0xff;
301                             if (b2 < b2Min || b2 > b2Max ||
302                                 (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
303                                 if (!isDoubleByte(b1, b2))
304                                     return CoderResult.malformedForLength(2);
305                                 return CoderResult.unmappableForLength(2);
306                             }
307                             inSize++;
308                         }
309                         if (dl - dp < 1)
310                             return CoderResult.OVERFLOW;
311 
312                         da[dp++] = c;
313                     }
314                     sp += inSize;
315                 }
316                 return CoderResult.UNDERFLOW;
317             } finally {
318                 src.position(sp - src.arrayOffset());
319                 dst.position(dp - dst.arrayOffset());
320             }
321         }
322 
323         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
324             int mark = src.position();
325             try {
326                 while (src.hasRemaining()) {
327                     int b1 = src.get() & 0xff;
328                     int inSize = 1;
329                     if (b1 == SO) {  // Shift out
330                         if (currentState != SBCS)
331                             return CoderResult.malformedForLength(1);
332                         else
333                             currentState = DBCS;
334                     } else if (b1 == SI) {
335                         if (currentState != DBCS)
336                             return CoderResult.malformedForLength(1);
337                         else
338                             currentState = SBCS;
339                     } else {
340                         char c = UNMAPPABLE_DECODING;
341                         if (currentState == SBCS) {
342                             c = b2cSB[b1];
343                             if (c == UNMAPPABLE_DECODING)
344                                 return CoderResult.unmappableForLength(1);
345                         } else {
346                             if (src.remaining() < 1)
347                                 return CoderResult.UNDERFLOW;
348                             int b2 = src.get()&0xff;
349                             if (b2 < b2Min || b2 > b2Max ||
350                                 (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) {
351                                 if (!isDoubleByte(b1, b2))
352                                     return CoderResult.malformedForLength(2);
353                                 return CoderResult.unmappableForLength(2);
354                             }
355                             inSize++;
356                         }
357 
358                         if (dst.remaining() < 1)
359                             return CoderResult.OVERFLOW;
360 
361                         dst.put(c);
362                     }
363                     mark += inSize;
364                 }
365                 return CoderResult.UNDERFLOW;
366             } finally {
367                 src.position(mark);
368             }
369         }
370     }
371 
372     // EBCDIC_DBCS_ONLY
373     public static class Decoder_EBCDIC_DBCSONLY extends Decoder {
374         static final char[] b2cSB;
375         static {
376             b2cSB = new char[0x100];
377             Arrays.fill(b2cSB, UNMAPPABLE_DECODING);
378         }
379         Decoder_EBCDIC_DBCSONLY(Charset cs, char[][] b2c, int b2Min, int b2Max) {
380             super(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max);
381         }
382     }
383 
384     // EUC_SIMPLE
385     // The only thing we need to "override" is to check SS2/SS3 and
386     // return "malformed" if found
387     public static class Decoder_EUC_SIM extends Decoder {
388         private final int SS2 =  0x8E;
389         private final int SS3 =  0x8F;
390 
391         Decoder_EUC_SIM(Charset cs,
392                         char[][] b2c, char[] b2cSB, int b2Min, int b2Max) {
393             super(cs, b2c, b2cSB, b2Min, b2Max);
394         }
395 
396         // No support provided for G2/G3 for SimpleEUC
397         protected CoderResult crMalformedOrUnderFlow(int b) {
398             if (b == SS2 || b == SS3 )
399                 return CoderResult.malformedForLength(1);
400             return CoderResult.UNDERFLOW;
401         }
402 
403         protected CoderResult crMalformedOrUnmappable(int b) {
404             if (b == SS2 || b == SS3 )
405                 return CoderResult.malformedForLength(1);
406             return CoderResult.unmappableForLength(2);
407         }
408     }
409 
410     public static class Encoder extends CharsetEncoder {
411         final int MAX_SINGLEBYTE = 0xff;
412         private final char[] c2b;
413         private final char[] c2bIndex;
414         Surrogate.Parser sgp;
415 
416         protected Encoder(Charset cs, char[] c2b, char[] c2bIndex) {
417             super(cs, 2.0f, 2.0f);
418             this.c2b = c2b;
419             this.c2bIndex = c2bIndex;
420         }
421 
422         Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) {
423             super(cs, avg, max, repl);
424             this.c2b = c2b;
425             this.c2bIndex = c2bIndex;
426         }
427 
428         public boolean canEncode(char c) {
429             return encodeChar(c) != UNMAPPABLE_ENCODING;
430         }
431 
432         Surrogate.Parser sgp() {
433             if (sgp == null)
434                 sgp = new Surrogate.Parser();
435             return sgp;
436         }
437 
438         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
439             char[] sa = src.array();
440             int sp = src.arrayOffset() + src.position();
441             int sl = src.arrayOffset() + src.limit();
442 
443             byte[] da = dst.array();
444             int dp = dst.arrayOffset() + dst.position();
445             int dl = dst.arrayOffset() + dst.limit();
446 
447             try {
448                 while (sp < sl) {
449                     char c = sa[sp];
450                     int bb = encodeChar(c);
451                     if (bb == UNMAPPABLE_ENCODING) {
452                         if (Character.isSurrogate(c)) {
453                             if (sgp().parse(c, sa, sp, sl) < 0)
454                                 return sgp.error();
455                             return sgp.unmappableResult();
456                         }
457                         return CoderResult.unmappableForLength(1);
458                     }
459 
460                     if (bb > MAX_SINGLEBYTE) {    // DoubleByte
461                         if (dl - dp < 2)
462                             return CoderResult.OVERFLOW;
463                         da[dp++] = (byte)(bb >> 8);
464                         da[dp++] = (byte)bb;
465                     } else {                      // SingleByte
466                         if (dl - dp < 1)
467                             return CoderResult.OVERFLOW;
468                         da[dp++] = (byte)bb;
469                     }
470 
471                     sp++;
472                 }
473                 return CoderResult.UNDERFLOW;
474             } finally {
475                 src.position(sp - src.arrayOffset());
476                 dst.position(dp - dst.arrayOffset());
477             }
478         }
479 
480         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
481             int mark = src.position();
482             try {
483                 while (src.hasRemaining()) {
484                     char c = src.get();
485                     int bb = encodeChar(c);
486                     if (bb == UNMAPPABLE_ENCODING) {
487                         if (Character.isSurrogate(c)) {
488                             if (sgp().parse(c, src) < 0)
489                                 return sgp.error();
490                             return sgp.unmappableResult();
491                         }
492                         return CoderResult.unmappableForLength(1);
493                     }
494                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
495                         if (dst.remaining() < 2)
496                             return CoderResult.OVERFLOW;
497                         dst.put((byte)(bb >> 8));
498                         dst.put((byte)(bb));
499                     } else {
500                         if (dst.remaining() < 1)
501                         return CoderResult.OVERFLOW;
502                         dst.put((byte)bb);
503                     }
504                     mark++;
505                 }
506                 return CoderResult.UNDERFLOW;
507             } finally {
508                 src.position(mark);
509             }
510         }
511 
512         protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
513             if (src.hasArray() && dst.hasArray())
514                 return encodeArrayLoop(src, dst);
515             else
516                 return encodeBufferLoop(src, dst);
517         }
518 
519         public int encodeChar(char ch) {
520             return c2b[c2bIndex[ch >> 8] + (ch & 0xff)];
521         }
522 
523         // init the c2b and c2bIndex tables from b2c.
524         static void initC2B(String[] b2c, String b2cSB, String b2cNR,  String c2bNR,
525                             int b2Min, int b2Max,
526                             char[] c2b, char[] c2bIndex)
527         {
528             Arrays.fill(c2b, (char)UNMAPPABLE_ENCODING);
529             int off = 0x100;
530 
531             char[][] b2c_ca = new char[b2c.length][];
532             char[] b2cSB_ca = null;
533             if (b2cSB != null)
534                 b2cSB_ca = b2cSB.toCharArray();
535 
536             for (int i = 0; i < b2c.length; i++) {
537                 if (b2c[i] == null)
538                     continue;
539                 b2c_ca[i] = b2c[i].toCharArray();
540             }
541 
542             if (b2cNR != null) {
543                 int j = 0;
544                 while (j < b2cNR.length()) {
545                     char b  = b2cNR.charAt(j++);
546                     char c  = b2cNR.charAt(j++);
547                     if (b < 0x100 && b2cSB_ca != null) {
548                         if (b2cSB_ca[b] == c)
549                             b2cSB_ca[b] = UNMAPPABLE_DECODING;
550                     } else {
551                         if (b2c_ca[b >> 8][(b & 0xff) - b2Min] == c)
552                             b2c_ca[b >> 8][(b & 0xff) - b2Min] = UNMAPPABLE_DECODING;
553                     }
554                 }
555             }
556 
557             if (b2cSB_ca != null) {      // SingleByte
558                 for (int b = 0; b < b2cSB_ca.length; b++) {
559                     char c = b2cSB_ca[b];
560                     if (c == UNMAPPABLE_DECODING)
561                         continue;
562                     int index = c2bIndex[c >> 8];
563                     if (index == 0) {
564                         index = off;
565                         off += 0x100;
566                         c2bIndex[c >> 8] = (char)index;
567                     }
568                     c2b[index + (c & 0xff)] = (char)b;
569                 }
570             }
571 
572             for (int b1 = 0; b1 < b2c.length; b1++) {  // DoubleByte
573                 char[] db = b2c_ca[b1];
574                 if (db == null)
575                     continue;
576                 for (int b2 = b2Min; b2 <= b2Max; b2++) {
577                     char c = db[b2 - b2Min];
578                     if (c == UNMAPPABLE_DECODING)
579                         continue;
580                     int index = c2bIndex[c >> 8];
581                     if (index == 0) {
582                         index = off;
583                         off += 0x100;
584                         c2bIndex[c >> 8] = (char)index;
585                     }
586                     c2b[index + (c & 0xff)] = (char)((b1 << 8) | b2);
587                 }
588             }
589 
590             if (c2bNR != null) {
591                 // add c->b only nr entries
592                 for (int i = 0; i < c2bNR.length(); i += 2) {
593                     char b = c2bNR.charAt(i);
594                     char c = c2bNR.charAt(i + 1);
595                     int index = (c >> 8);
596                     if (c2bIndex[index] == 0) {
597                         c2bIndex[index] = (char)off;
598                         off += 0x100;
599                     }
600                     index = c2bIndex[index] + (c & 0xff);
601                     c2b[index] = b;
602                 }
603             }
604         }
605     }
606 
607     // EBCDIC_DBCS_ONLY
608     public static class Encoder_EBCDIC_DBCSONLY extends Encoder {
609         Encoder_EBCDIC_DBCSONLY(Charset cs, byte[] repl,
610                                 char[] c2b, char[] c2bIndex) {
611             super(cs, 2.0f, 2.0f, repl, c2b, c2bIndex);
612         }
613 
614         public int encodeChar(char ch) {
615             int bb = super.encodeChar(ch);
616             if (bb <= MAX_SINGLEBYTE)
617                 return UNMAPPABLE_ENCODING;
618             return bb;
619         }
620     }
621 
622     // for IBM_EBCDIC_DBCS
623     public static class Encoder_EBCDIC extends Encoder {
624         static final int SBCS = 0;
625         static final int DBCS = 1;
626         static final byte SO = 0x0e;
627         static final byte SI = 0x0f;
628 
629         protected int  currentState = SBCS;
630 
631         Encoder_EBCDIC(Charset cs, char[] c2b, char[] c2bIndex) {
632             super(cs, 4.0f, 5.0f, new byte[] {(byte)0x6f}, c2b, c2bIndex);
633         }
634 
635         protected void implReset() {
636             currentState = SBCS;
637         }
638 
639         protected CoderResult implFlush(ByteBuffer out) {
640             if (currentState == DBCS) {
641                 if (out.remaining() < 1)
642                     return CoderResult.OVERFLOW;
643                 out.put(SI);
644             }
645             implReset();
646             return CoderResult.UNDERFLOW;
647         }
648 
649         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
650             char[] sa = src.array();
651             int sp = src.arrayOffset() + src.position();
652             int sl = src.arrayOffset() + src.limit();
653             byte[] da = dst.array();
654             int dp = dst.arrayOffset() + dst.position();
655             int dl = dst.arrayOffset() + dst.limit();
656 
657             try {
658                 while (sp < sl) {
659                     char c = sa[sp];
660                     int bb = encodeChar(c);
661                     if (bb == UNMAPPABLE_ENCODING) {
662                         if (Character.isSurrogate(c)) {
663                             if (sgp().parse(c, sa, sp, sl) < 0)
664                                 return sgp.error();
665                             return sgp.unmappableResult();
666                         }
667                         return CoderResult.unmappableForLength(1);
668                     }
669                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
670                         if (currentState == SBCS) {
671                             if (dl - dp < 1)
672                                 return CoderResult.OVERFLOW;
673                             currentState = DBCS;
674                             da[dp++] = SO;
675                         }
676                         if (dl - dp < 2)
677                             return CoderResult.OVERFLOW;
678                         da[dp++] = (byte)(bb >> 8);
679                         da[dp++] = (byte)bb;
680                     } else {                    // SingleByte
681                         if (currentState == DBCS) {
682                             if (dl - dp < 1)
683                                 return CoderResult.OVERFLOW;
684                             currentState = SBCS;
685                             da[dp++] = SI;
686                         }
687                         if (dl - dp < 1)
688                             return CoderResult.OVERFLOW;
689                         da[dp++] = (byte)bb;
690 
691                     }
692                     sp++;
693                 }
694                 return CoderResult.UNDERFLOW;
695             } finally {
696                 src.position(sp - src.arrayOffset());
697                 dst.position(dp - dst.arrayOffset());
698             }
699         }
700 
701         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
702             int mark = src.position();
703             try {
704                 while (src.hasRemaining()) {
705                     char c = src.get();
706                     int bb = encodeChar(c);
707                     if (bb == UNMAPPABLE_ENCODING) {
708                         if (Character.isSurrogate(c)) {
709                             if (sgp().parse(c, src) < 0)
710                                 return sgp.error();
711                             return sgp.unmappableResult();
712                         }
713                         return CoderResult.unmappableForLength(1);
714                     }
715                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
716                         if (currentState == SBCS) {
717                             if (dst.remaining() < 1)
718                                 return CoderResult.OVERFLOW;
719                             currentState = DBCS;
720                             dst.put(SO);
721                         }
722                         if (dst.remaining() < 2)
723                             return CoderResult.OVERFLOW;
724                         dst.put((byte)(bb >> 8));
725                         dst.put((byte)(bb));
726                     } else {                  // Single-byte
727                         if (currentState == DBCS) {
728                             if (dst.remaining() < 1)
729                                 return CoderResult.OVERFLOW;
730                             currentState = SBCS;
731                             dst.put(SI);
732                         }
733                         if (dst.remaining() < 1)
734                             return CoderResult.OVERFLOW;
735                         dst.put((byte)bb);
736                     }
737                     mark++;
738                 }
739                 return CoderResult.UNDERFLOW;
740             } finally {
741                 src.position(mark);
742             }
743         }
744     }
745 
746     // EUC_SIMPLE
747     public static class Encoder_EUC_SIM extends Encoder {
748         Encoder_EUC_SIM(Charset cs, char[] c2b, char[] c2bIndex) {
749             super(cs, c2b, c2bIndex);
750         }
751     }
752 }